<template>
    <div ref="app" class="page" >
      <div class="container" :style="`width:${width}px;height:${height}px`">
        <img :src="src" alt="标注图片" ref="image">
        <canvas id="canvas" @click="handleClick" @dblclick="handleDBClick" @mousedown="handleMouseDown" @mouseup="handleMouseUp" @mousemove="handleMouseMove" @contextmenu.prevent.stop="showMenu" :style="`cursor:${cursor}`" />
        <ul class="tips" v-show="isShowTips" :style="`left:${pointObj.x}px;top:${pointObj.y}px`">
          <li>
            <button @click="handleDelete">删除</button>
          </li>
          <li>
            <button @click="handleAddNote">更新备注</button>
          </li>
        </ul>
      </div>
      <button @click="drawStart('point')" style="margin-right:10px;">打点标注</button>
      <button @click="drawStart('square')">矩形标注</button>
      <input type="text" v-model="note" v-show="showNote" @keyup.enter="handleEnter" ref="note" class="note">
    </div>
  </template>
  <script>
  import pen from '@/assets/images/pen.png'
  export default {
    data() {
      return {
        isShowTips:false,
        image:null,
        ctx:null,
        w:null,
        h:null,
        initCanvas:false,
        drawType:"",
        cursor:'default',
        pen,
        tempPointArr:[],
        pointData:[],
        historyImageData:[],
        startPoint:null,
        timer:null,
        selectedIndex:0,
        pointObj:{},
        imgData:null,
        note:'',
        showNote:false,
        textObj:{}
      };
    },
    props:{
      src:String,
      width:Number,
      height:Number | String
    },
    methods: {
      // 初始化canvas
      init(){
        let canvas = document.getElementById('canvas')
        this.image = this.$refs.image
        canvas.width=this.image.width
        canvas.height=this.image.height
        this.ctx = canvas.getContext('2d')
        this.$refs.app.appendChild(canvas)
        this.w =this.image.width
        this.h=this.image.offsetHeight
      },
      // 选择标注类型
      drawStart(type){
        if(!this.initCanvas){
          this.ctx.drawImage(this.image,0,0,this.w,this.h)
          this.initCanvas = true
        }
        this.drawType = type
        if(this.drawType==='point'){
          this.cursor=`url(${pen}) 0 32,url(${pen}),auto`;
        }else{
          this.cursor="crosshair"
        }
        this.ctx.lineWidth = 1
      },

      handleClick(e){
        if(this.isShowTips){
          this.isShowTips = false
          this.ctx.putImageData(this.historyImageData[this.historyImageData.length - 1],0,0)
        }
        // 使用点标注
        if(this.drawType === 'point'){
          clearTimeout(this.timer)
          this.timer = setTimeout(()=>{
            this.tempPointArr.push({
              x:e.offsetX,
              y:e.offsetY
            })
            // 绘制矩形小点
            this.ctx.fillStyle="#FF0000";
            this.ctx.fillRect(e.offsetX - 2, e.offsetY - 2, 4, 4);
            if(this.tempPointArr.length <=1) return

            // 连线
            this.ctx.beginPath()
            this.ctx.strokeStyle= '#FF0000'
            this.ctx.moveTo(this.tempPointArr[this.tempPointArr.length - 2].x,this.tempPointArr[this.tempPointArr.length - 2].y);
            this.ctx.lineTo(this.tempPointArr[this.tempPointArr.length - 1].x,this.tempPointArr[this.tempPointArr.length - 1].y);
            this.ctx.stroke();
            if(this.tempPointArr.length >2 &&this.tempPointArr[this.tempPointArr.length - 1].x == this.tempPointArr[0].x && this.tempPointArr[this.tempPointArr.length - 1].y == this.tempPointArr[0].y){
              this.drawPointEnd()
            }
          },200)
        }
      },
      handleDBClick(){
        if(this.tempPointArr.length <= 2) return
        clearTimeout(this.timer)
        this.ctx.moveTo(this.tempPointArr[this.tempPointArr.length - 1].x,this.tempPointArr[this.tempPointArr.length - 1].y);
        this.ctx.lineTo(this.tempPointArr[0].x,this.tempPointArr[0].y);
        this.ctx.stroke();
        this.pointData.push({
          'point':this.tempPointArr
        })
        this.tempPointArr = []
        this.historyImageData.push(this.ctx.getImageData(0,0,this.w,this.h))
      },
      handleMouseDown(e){
        if(e.button===2) return
        if(this.drawType === 'square'){
          this.startPoint = {
            x:e.offsetX,
            y:e.offsetY
          }
          this.imgData = this.ctx.getImageData(0,0,this.w,this.h)
        }
      },
      handleMouseMove(e){
        if(!this.startPoint){
          return
        }
        this.ctx.putImageData(this.imgData,0,0)
        this.ctx.beginPath()
        this.ctx.fillStyle = "rgba(255,0,0,0.1)";
        this.ctx.strokeStyle = '#FF0000';
        this.ctx.strokeRect(this.startPoint.x,this.startPoint.y,e.offsetX - this.startPoint.x,e.offsetY - this.startPoint.y);
      },

      handleMouseUp(e){
        if(!this.startPoint) return
        if(this.drawType === 'square'){
          this.historyImageData.push(this.ctx.getImageData(0,0,this.w,this.h))
          this.pointData.push({
            'point':[
              {
                x:this.startPoint.x,
                y:this.startPoint.y
              },{
                x:e.offsetX,
                y:this.startPoint.y
              },{
                x:e.offsetX,
                y:e.offsetY
              },{
                x:this.startPoint.x,
                y:e.offsetY
              }
            ]
          })
          this.startPoint = null
        }
      },

      // 右键弹出菜单
      showMenu(e){
        if(!this.pointData.length) return
        this.cursor="default"
        this.drawType = ''
        this.ctx.putImageData(this.historyImageData[this.historyImageData.length - 1],0,0)
        const index = this.pointData.findIndex((item)=>{
          let leftX = false
          let rightX = false
          let topY = false
          let bottomY = false
          item.point.forEach(p=>{
            if(e.offsetX > p.x){
              leftX = true
            }
            if(e.offsetX < p.x){
              rightX = true
            }
            if(e.offsetY < p.y){
              bottomY = true
            }
            if(e.offsetY > p.y){
              topY = true
            }
          })
          return leftX && rightX && topY && bottomY
        })
        if(index > -1){
          this.pointObj={x:e.offsetX,y:e.offsetY}
          this.isShow  = true
          this.fillRect(this.pointData[index].point)
          this.selectedIndex = index
          this.isShowTips=true
        }

      },
      // 选中填充背景色
      fillRect(pointArr){
        this.ctx.beginPath()
        this.ctx.fillStyle ='rgba(255,0,0,0.1)'
        this.ctx.moveTo(pointArr[0].x,pointArr[0].y);
        for(let i =1;i<pointArr.length;i++){
          this.ctx.lineTo(pointArr[i].x,pointArr[i].y);
        }
        this.ctx.fill()
      },
      // 删除选中内容
      handleDelete(){
        this.ctx.putImageData(this.historyImageData[this.historyImageData.length - 1],0,0)
        this.ctx.beginPath()
        this.ctx.globalCompositeOperation = 'destination-out'
        const data = this.pointData[this.selectedIndex].point
        this.ctx.lineWidth = 5
        this.ctx.moveTo(data[0].x-3,data[0].y-2)
        for(let i=1;i<data.length;i++){
          this.ctx.lineTo(data[i].x,data[i].y)
          this.ctx.stroke()
        }
        this.ctx.lineTo(data[0].x,data[0].y)
        this.ctx.stroke()
        this.pointData.splice(this.selectedIndex,1)
        this.historyImageData.push(this.ctx.getImageData(0,0,this.w,this.h))
        this.isShowTips= false
        this.ctx.globalCompositeOperation = 'source-over'
      },
      // 添加备注
      handleAddNote(){
        this.ctx.putImageData(this.historyImageData[this.historyImageData.length - 1],0,0)
        this.note =""
        this.showNote=true
        this.$nextTick(()=>{
          this.isShowTips = false
          this.$refs.note.focus()
        })
      },
      handleEnter(){
        //先清除原来的文本
        let textW = this.ctx.measureText(this.textObj[this.selectedIndex]).width
        const data = [].concat(this.pointData[this.selectedIndex].point)
        let textLeft = data.sort((a,b)=>a.x-b.x)[0].x
        let textTop = data.sort((a,b)=>a.y-b.y)[0].y
        this.ctx.clearRect(textLeft + 5,textTop - 8 - 12 ,textW,13)
        this.ctx.fillStyle ='#FF0000';
        this.ctx.font="12px Arial"
        this.ctx.fillText(this.note,textLeft + 5,textTop - 8)
        this.textObj[this.selectedIndex] = this.note
        this.showNote = false
        this.historyImageData.push(this.ctx.getImageData(0,0,this.w,this.h))
      },
    },
    mounted() {
      this.init()
    }
  };
  </script>

  <style lang="scss" scoped>
  .page{
    position: relative;
    canvas{
      position: absolute;
      top:0;
      left:0
    }
    button{
      margin-top:20px;
    }
    .note{
      position: absolute;
      top:50%;
      left:50%;
      transform: translate(-50%,-50%);
      z-index:99;
      border:1px solid #333;
      height:30px;
      width:200px;
    }
  }
  .container{
    position: relative;
    img{
      width:100%;
      height:100%;
      display: block;
    }
    .tips{
      position: absolute;
      list-style: none;
      margin:0;
      padding:0;
      z-index:9;
      // border:1px solid #ccc;
      li{
        button{
          margin:5px 0;
          width:80px;
          text-align: center;
        }
      }
    }
  }
  </style>
